package launcher.simple.android.woblog.cn.system;

import java.util.List;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProfile.ServiceListener;
import android.bluetooth.peripheral.BluetoothHfDevice;
import android.bluetooth.peripheral.CallStateInfo;
import android.bluetooth.peripheral.ClccInfo;
import android.bluetooth.peripheral.IHfDeviceEventHandler;
import android.bluetooth.peripheral.PhoneBookInfo;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;

import launcher.simple.android.woblog.cn.simplelauncher.R;

public class InCallManager implements ServiceListener {

	private static final String ACTION_PHONE_RECEIVE = "cn.ingenic.indroidsync.action.PHONE_RECEIVE";
	public static final String ACTION_OUT_CALL = "cn.ingenic.incall.action.OUT_CALL";
	public static final String ACTION_INCOMING_CALL = "cn.ingenic.incall.action.INCOMING_CALL";
	public static final String PREFERENCE_BOND = "bond_preference";
	public static final String PREFERENCE_BT = "bt_connected";
	public static final String PREFERENCE_SYNC = "sync_bond";
	private static final String EXTRA_RECEIVE_CODE = "code";
	public static final String BOND_VALUE = "indroidsync_bond";
	private static final int MSG_RING = -4;
	private static final int MSG_ANSWER = -3;
	private static final int MSG_DIAL = -2;
	private static final int MSG_DISCONNECT_AUDIO = -1;
	private static final int MSG_FORCE_FINISH = 0;
	private static final int GUI_UPDATE_CALL_STATUS = 2;
	private static final int GUI_UPDATE_AUDIO_STATE = 5;
	private static final int GUI_UPDATE_VOLUME = 8;

	private static final int PHONE_REJECT = 21;
	private static final int PHONE_ACCEPT = 22;
	private static final int PHONE_SMS = 31;
	private static final int PHONE_IGNORE = -100;

	private static final String TAG = "BtHfManager";
	private static InCallManager mInstance;
	private BluetoothHfDevice mHfDevice;
	private Context mContext;
	private CallInfo mInfo;
	private CallInfo mWaitingInfo;
	private Ringer mRinger;
	private int mPhoneState;
	private AudioManager mAg;
	private OnPhoneStateChangedListener mListener;
	private OnInfoChangedListener mChangedListener;
	private boolean isManual = false;
	private boolean isManualMute = false;
	private boolean isInitialized = false;
	private OnServiceListener mServiceListener;
	private final Intent mIntent;

	private InCallManager(Context context) {
		mContext = context;
		mIntent = new Intent(ACTION_PHONE_RECEIVE);
		mRinger = Ringer.init(context);
		mAg = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
		isInitialized = false;
	}

	public static void initialize(Context context) {
		if (mInstance == null) {
			mInstance = new InCallManager(context);
		}
		mInstance.initialize();
	}

	private void initialize() {
		if (mHfDevice == null) {
			Log.i(TAG, "initialize");
			BluetoothHfDevice.getProxy(mContext, this);
		}
	}

	public static InCallManager getDefault() {
		if (mInstance == null) {
			throw new NullPointerException("Must initialize before getDefault!");
		}
		return mInstance;
	}

	public boolean isBond() {
		SharedPreferences sp = mContext.getSharedPreferences(PREFERENCE_BOND, Context.MODE_PRIVATE);
		return sp.getBoolean(PREFERENCE_SYNC, false);
	}

	public boolean isBtConnected() {
		SharedPreferences sp = mContext.getSharedPreferences(PREFERENCE_BOND, Context.MODE_PRIVATE);
		return sp.getBoolean(PREFERENCE_BT, false);
	}

	public boolean isInitialized() {
		return isInitialized;
	}

	private void checkout() {
		boolean isConnected = false;
		if (mHfDevice.getConnectedDevices().size() == 0) {
			isConnected = false;
		} else {
			isConnected = true;
		}
		SharedPreferences sp = mContext.getSharedPreferences(PREFERENCE_BOND, Context.MODE_PRIVATE);
		sp.edit().putBoolean(PREFERENCE_BT, isConnected).commit();
	}

	public void setOnPhoneStateChangedListener(OnPhoneStateChangedListener l) {
		mListener = l;
	}

	public void setOnInfoChangedListener(OnInfoChangedListener l) {
		mChangedListener = l;
	}

	public synchronized void prepareIncoming() {
	    if (mPhoneState != PhoneState.STATE_IDLE) {
            return;
        }
	    setPhoneState(PhoneState.STATE_PREPARE);
    }

	public synchronized void incoming(CallInfo info) {
	    Log.i(TAG, "incoming:" + mPhoneState);
		if (mPhoneState != PhoneState.STATE_PREPARE) {
			return;
		}
//		if (isBtConnected() && mHfDevice != null) {
//			mHfDevice.disconnectAudio();
//		}
//		updateAudioState(BluetoothHfDevice.STATE_AUDIO_DISCONNECTED);
		if (info == null) {
			return;
		}
		setCallInfo(info);
		Intent it = new Intent(ACTION_INCOMING_CALL);
		it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		mContext.startActivity(it);
	}

	public void setCallInfo(CallInfo info) {
		mInfo = info;
		if (mChangedListener != null) {
			mChangedListener.onInfoChanged(info);
		}
	}

	public CallInfo getRemoteInfo() {
		return mInfo;
	}

	@Override
	public void onServiceConnected(int profile, BluetoothProfile proxy) {
		Log.i(TAG, "onServiceConnected");
		mHfDevice = (BluetoothHfDevice) proxy;
		mHfDevice.registerEventHandler(new HfDeviceHandler());
		isInitialized = true;
		checkout();
		if (mServiceListener != null) {
			mServiceListener.onServiceConnected();
		}
	}

	@Override
	public void onServiceDisconnected(int profile) {
		Log.i(TAG, "onServiceDisconnected");
		mHfDevice = null;
		isInitialized = false;
		if (mServiceListener != null) {
			mServiceListener.onServiceDisconnected();
		}
	}

	private synchronized void setPhoneState(int state) {
		if (state == PhoneState.STATE_IDLE) {
			isManual = false;
			isManualMute = false;
			if (mPrivateHandler.hasMessages(MSG_FORCE_FINISH)) {
				mPrivateHandler.removeMessages(MSG_FORCE_FINISH);
			}
		}
		if (mPhoneState == state) {
		    return;
		}
		mPhoneState = state;
		if (mListener != null) {
			mListener.onPhoneStateChanged(state);
		}
		notifyCallState(state);
	}

	public int getPhoneState() {
		return mPhoneState;
	}

	public void onRing() {
	    if (mPhoneState != PhoneState.STATE_PREPARE) {
            return;
        }
		setPhoneState(PhoneState.STATE_RINGING);
		mPrivateHandler.sendEmptyMessage(MSG_RING);
//		if (!mRinger.isRinging()) {
//			mRinger.ring();
//		}
	}

	public void dial(CallInfo info) {
		setCallInfo(info);
		setPhoneState(PhoneState.STATE_DIALING);
		mPrivateHandler.sendEmptyMessage(MSG_DIAL);
	}

	private void onDialing() {
		if (!isBtConnected()) {
			Toast.makeText(mContext, R.string.con_bt, Toast.LENGTH_SHORT)
					.show();
			onHangup();
			return;
		}
        if (mInfo!=null) {
            isManual = true;
            if (!mHfDevice.dial(mInfo.getRemoteNumber())) {
                setPhoneState(PhoneState.STATE_IDLE);
            } else {
                setPhoneState(PhoneState.STATE_DIALING);
            }
        } else {
            Log.i(TAG,"onDialing,mInfo is null,set PhoneState to idle");
        }
	}

	private void onOffhook() {
		if (mRinger.isRinging()) {
			mRinger.stopRing();
		}
		if (isManual) {
			mHfDevice.connectAudio();
			updateAudioState(BluetoothHfDevice.STATE_AUDIO_CONNECTED);
		} else {
			mPrivateHandler.sendEmptyMessageDelayed(MSG_DISCONNECT_AUDIO, 1000);
		}
		setPhoneState(PhoneState.STATE_OFFHOOK);
	}

	public void onHangup() {
		if (mRinger.isRinging()) {
			mRinger.stopRing();
		}
		updateAudioState(BluetoothHfDevice.STATE_AUDIO_DISCONNECTED);
		setPhoneState(PhoneState.STATE_IDLE);
		if (mInfo != null) {
			mInfo.clean();
			mInfo = null;
		}
	}

	private Handler mPrivateHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case MSG_RING:
				if (isInitialized) {
					if (mPhoneState != PhoneState.STATE_RINGING) {
						return;
					}
					if (!mRinger.isRinging()) {
						mRinger.ring();
					}
				} else {
					sendMessageDelayed(msg, 100);
				}
				break;
			case MSG_ANSWER:
				answer();
				break;
			case MSG_DIAL: {
				if (isInitialized) {
					onDialing();
				} else {
					sendMessageDelayed(msg, 100);
				}
				break;
			}
			case MSG_DISCONNECT_AUDIO:
				mHfDevice.disconnectAudio();
				break;
			case MSG_FORCE_FINISH:
			    Log.i(TAG, "force finish");
				onHangup();
				break;
			case GUI_UPDATE_CALL_STATUS:
				CallStateInfo callInfo = (CallStateInfo) msg.obj;
				int status = callInfo.getCallSetupState();
				String number = callInfo.getPhoneNumber();
//				if (status != BluetoothHfDevice.CALL_SETUP_STATE_WAITING && number != null && mInfo == null) {
//					mInfo = new CallInfo(mContext, null, number);
//					if (mChangedListener != null) {
//						mChangedListener.onInfoChanged(mInfo);
//					}
//				}
				switch (status) {
				case BluetoothHfDevice.CALL_SETUP_STATE_INCOMING:
				    Log.i(TAG, "incoming:state:" + mPhoneState);
					if (mPhoneState == PhoneState.STATE_RINGING) {
						return;
					}
					prepareIncoming();
					CallInfo info = mInfo;
					if (number != null && info == null) {
						info = new CallInfo(mContext, null, number);
					}
					incoming(info);
					break;
				case BluetoothHfDevice.CALL_SETUP_STATE_DIALING:
				case BluetoothHfDevice.CALL_SETUP_STATE_ALERTING:
					if (isManual) {
						mHfDevice.connectAudio();
						updateAudioState(BluetoothHfDevice.STATE_AUDIO_CONNECTED);
					} else {
						mHfDevice.disconnectAudio();
					}
					break;
				case BluetoothHfDevice.CALL_SETUP_STATE_WAITING:
					mWaitingInfo = new CallInfo(mContext, null, number);
					mWaitingInfo.setPhoneState(PhoneState.STATE_WAITING);
					break;
				case BluetoothHfDevice.CALL_SETUP_STATE_IDLE:
					if (callInfo.isInCall()) {
						onOffhook();
					} else {
						onHangup();
					}
					break;
				default:
					break;
				}
				break;
			case GUI_UPDATE_AUDIO_STATE:
				updateAudioState(msg.arg1);
				break;
			case GUI_UPDATE_VOLUME:
				int type = msg.arg1;
				int volume = msg.arg2;
				if (type == BluetoothHfDevice.VOLUME_TYPE_SPK) {
					bluetooth_set_volume(String.valueOf(volume));
				}
				break;
			default:
				break;
			}
		};
	};

	private void updateAudioState(int state) {
		if (state == BluetoothHfDevice.STATE_AUDIO_CONNECTED
				|| state == BluetoothHfDevice.STATE_AUDIO_DISCONNECTED) {
			if (mAg.getMode() != state) {
				Log.i(TAG, "setMode-----old:" + mAg.getMode() + ",new:" + state);
				mAg.setMode(state);
			}
		}
	}

	private class HfDeviceHandler implements IHfDeviceEventHandler {

		@Override
		public void onAudioStateChange(int newState, int prevState) {
			Log.i(TAG, "onAudioStateChange-----newState:" + newState
					+ ",prevState:" + prevState);
			mPrivateHandler.obtainMessage(GUI_UPDATE_AUDIO_STATE, newState, -1)
					.sendToTarget();
		}

		@Override
		public void onCLCCRsp(int arg0, List<ClccInfo> arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onCallStateChange(int status, int callSetupState,
				int numActive, int numHeld, String number, int addrType) {
			Log.i(TAG, "onCallStateChange:state == " + callSetupState + "numHeld:" + numHeld + "numActive:" + numActive + "number:" + number + "addrType:" + addrType);
			if (BluetoothHfDevice.NO_ERROR == status) {
				CallStateInfo info = new CallStateInfo(numActive,
						callSetupState, numHeld, number);
				mPrivateHandler.obtainMessage(GUI_UPDATE_CALL_STATUS, info)
						.sendToTarget();
			} else {
				Log.e(TAG, "Call status failed:" + status);

				Toast.makeText(mContext, "Call operation failed.",
						Toast.LENGTH_LONG).show();
			}
		}

		@Override
		public void onConnectionStateChange(int arg0, BluetoothDevice arg1,
				int arg2, int arg3) {
			// TODO Auto-generated method stub

		}

//		@Override
//		public void onExtendedErrorResult(int arg0) {
//			// TODO Auto-generated method stub
//
//		}

		@Override
		public void onIndicatorsUpdate(int[] arg0) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onOperatorSelectionRsp(int arg0, int arg1, String arg2) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onPhoneBookReadRsp(int arg0, List<PhoneBookInfo> arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onRingEvent() {
			// TODO Auto-generated method stub

		}

		@Override
		public void onSubscriberInfoRsp(int arg0, String arg1, int arg2) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onVRStateChange(int arg0, int arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onVendorAtRsp(int arg0, String arg1) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onVolumeChange(int volType, int volume) {
			mPrivateHandler.obtainMessage(GUI_UPDATE_VOLUME, volType, volume)
					.sendToTarget();
		}

	}

	public void hangup() {
		if (mRinger.isRinging()) {
			mRinger.stopRing();
		}
//		setPhoneState(PhoneState.STATE_DISCONNECTING);
		if (isBtConnected()) {
			mHfDevice.hangup();
		} else {
			operateCall(PHONE_REJECT);
		}
		mPrivateHandler.sendEmptyMessageDelayed(MSG_FORCE_FINISH, 5000);
	}

	public void answer() {
//		setPhoneState(PhoneState.STATE_ACCEPTING);
		if (mRinger.isRinging()) {
			mRinger.stopRing();
		}
		isManual = true;
		if (!isInitialized) {
			mPrivateHandler.sendEmptyMessageDelayed(MSG_ANSWER, 100);
			return;
		}
		if (!isBtConnected() || !mHfDevice.answer()) {
			setPhoneState(PhoneState.STATE_IDLE);
		}
	}

	public void sendDTMFcode(char num) {
		mHfDevice.sendDTMFcode(num);
	}

	public interface OnInfoChangedListener {
		void onInfoChanged(CallInfo info);
	}

	public interface OnPhoneStateChangedListener {
		void onPhoneStateChanged(int state);
	}

	static {
		System.loadLibrary("bt_volume_control_jni");
	}

	static native boolean bluetooth_set_volume(String v);

	public void mute() {
		if (mRinger.isRinging()) {
			mRinger.stopRing();
		}
	}

	public void manualMute() {
		isManualMute = true;
		mute();
	}

	public void unMute() {
		if (isManualMute) {
			return;
		}
		if (!mRinger.isRinging()) {
			mRinger.ring();
		}
	}

	public void setOnServiceListener(OnServiceListener l) {
		mServiceListener = l;
	}

	public interface OnServiceListener {
		void onServiceConnected();

		void onServiceDisconnected();
	}

	private void operateCall(int operateCode) {
		Log.v(TAG, "operate the phone call:" + operateCode);
		mIntent.putExtra(EXTRA_RECEIVE_CODE, operateCode);
		mContext.sendBroadcast(mIntent);
	}

	public void receiveSMS(String sms) {
		mIntent.putExtra(PhoneState.EXTRA_NUMBER, mInfo.getRemoteNumber());
		mIntent.putExtra(PhoneState.EXTRA_SMS, sms);
		operateCall(PHONE_SMS);
		hangup();
	}

	private void notifyCallState(int state) {
		if (state == PhoneState.STATE_DIALING) {
			state = PhoneState.STATE_OFFHOOK;
		}
		if (state <= PhoneState.STATE_OFFHOOK) {
			Log.i(TAG, "notify:" + state);
			TelephonyManager tm = (TelephonyManager) mContext
					.getSystemService(Context.TELEPHONY_SERVICE);
//			if (mInfo == null) {
//				tm.setCallState(state, null);
//			} else {
//				tm.setCallState(state, mInfo.getRemoteNumber());
//			}
		}
	}
}
